<html>
<head>
    <script src="../OLLoader.js"></script>
    <script type="text/javascript">
    
    function test_initialize(t) {
        t.plan(3);
        var layer = {
            styleMap: {createSymbolizer: function(){}},
            events: {
                on: function() {},
                un: function() {}
            }
        };
        var options = {
            documentDrag: true
        };
        var control = new OpenLayers.Control.ModifyFeature(layer, options);
        
        t.ok(control.layer == layer,
             "constructor sets layer correctly");
        t.eq(control.handlers.drag.documentDrag, true,
             "constructor sets options correctly on drag handler");
        t.eq(control.mode, OpenLayers.Control.ModifyFeature.RESHAPE,
             "constructor initializes modification mode correctly");
        control.destroy();
    }

    function test_destroy(t) {
        t.plan(1);
        var map = new OpenLayers.Map("map");
        var layer = new OpenLayers.Layer.Vector();
        map.addLayer(layer);
        var control = new OpenLayers.Control.ModifyFeature(layer);
        control.destroy();
        t.eq(control.layer, null, "Layer reference removed on destroy.");
        map.destroy();
    }
    
    function test_activate(t) {
        t.plan(2);
        var map = new OpenLayers.Map("map");
        var layer = new OpenLayers.Layer.Vector();
        map.addLayer(layer);
        var control = new OpenLayers.Control.ModifyFeature(layer);
        map.addControl(control);
        t.ok(!control.handlers.drag.active,
             "drag handler is not active prior to activating control");
        control.activate();
        t.ok(control.handlers.drag.active,
             "drag handler is active after activating control");

        map.destroy();
    }

    function test_initDeleteCodes(t) {
        t.plan(3);
        var layer = new OpenLayers.Layer.Vector();
        var control = new OpenLayers.Control.ModifyFeature(layer, {'deleteCodes': 46});
        t.eq(control.deleteCodes[0], 46, "Delete code properly turned into an array.");
        var control = new OpenLayers.Control.ModifyFeature(layer);
        t.eq(control.deleteCodes[0], 46, "Default deleteCodes include delete"); 
        t.eq(control.deleteCodes[1], 68, "Default deleteCodes include 'd'"); 
        
        control.destroy();
        layer.destroy();
    }
    
    function test_handleKeypress(t) {
        t.plan(16);

        /**
         * There are two things that we want to test here
         * 1) test that control.deleteCodes are respected
         * 3) test that a vertex is properly deleted
         *
         * In the future, feature deletion may be added to the control.
         */
        
        var layer = new OpenLayers.Layer.Vector();
        var control = new OpenLayers.Control.ModifyFeature(layer);
        var delKey = 46;
        var dKey = 100;
        control.deleteCodes = [delKey, dKey];
        
        // test that a polygon vertex is deleted for all delete codes
        var point = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point()
        );
        var poly = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Polygon()
        );
        
        // mock up vertex deletion
        var origGetFeatureFromEvent = layer.getFeatureFromEvent;
        layer.getFeatureFromEvent = function() { return point; };
        control.feature = poly;
        // we cannot use selectFeature since the control is not part of a map
        control._originalGeometry = poly.geometry.clone();
        control.vertices = [point];
        point.geometry.parent = {
            removeComponent: function(geometry) {
                t.eq(geometry.id, point.geometry.id,
                     "vertex deletion: removeComponent called on parent with proper geometry");
            }
        };
        layer.events.on({
            "featuremodified": function(event) {
                t.ok(event.feature.modified !== null, "modified property of feature should have been set");
                t.eq(event.feature.id, poly.id, "vertex deletion: featuremodifed triggered");
            },
            "vertexremoved": function(evt) {
                layer.events.unregister("vertexremoved", this, arguments.callee);
                t.eq(evt.feature.id, poly.id, "vertexremoved triggered with correct feature");
                t.eq(evt.vertex.id, point.geometry.id, "vertexremoved triggered with correct vertex");
                t.eq(evt.pixel, "foo", "vertexremoved triggered with correct pixel");
            }
        });
        layer.drawFeature = function(feature) {
            t.eq(feature.id, poly.id,
                 "vertex deletion: drawFeature called with the proper feature");
        };
        control.resetVertices = function() {
            t.ok(true, "vertex deletion: resetVertices called");
        };
        control.onModification = function(feature) {
            t.eq(feature.id, poly.id,
                 "vertex deletion: onModification called with the proper feature");
        };
        // run the above four tests twice
        control.handleKeypress({keyCode:delKey, xy: "foo"});
        control.handleKeypress({keyCode:dKey});
        t.eq(control.feature.state, OpenLayers.State.UPDATE, "feature state set to update");

        // now make sure nothing happens if the vertex is mid-drag
        control.handlers.drag.dragging = true;
        control.handleKeypress({keyCode:delKey});

        //  clean up
        layer.getFeatureFromEvent = origGetFeatureFromEvent;
        control.destroy();
        layer.destroy();
    }    
        

    function test_onUnSelect(t) {
        t.plan(5);
        var layer = new OpenLayers.Layer.Vector();
        var control = new OpenLayers.Control.ModifyFeature(layer);
        var fakeFeature = {'id':'myid'};
        control.vertices = 'a';
        control.virtualVertices = 'b';
        control.features = true;
        layer.events.on({"afterfeaturemodified": function(event) {
            t.eq(event.feature, fakeFeature, "afterfeaturemodified triggered");
        }});
        control.onModificationEnd = function (feature) { t.eq(feature.id, fakeFeature.id, "onModificationEnd got feature.") }
        layer.removeFeatures = function(verts) {
            t.ok(verts == 'a', "Normal verts removed correctly");
        }
        layer.destroyFeatures = function(verts) {
            t.ok(verts == 'b', "Virtual verts destroyed correctly");
        }
        control.unselectFeature(fakeFeature);
        t.eq(control.feature, null, "feature is set to null");
        
        layer.destroyFeatures = function() {};        
        control.destroy();
        layer.destroy();
    }
    
    function test_stop_modification(t) {
        t.plan(1);
        var map = new OpenLayers.Map('map');
        var layer = new OpenLayers.Layer.Vector("Vectors!", {isBaseLayer: true});
        var feature = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(0, 0)
        );
        layer.addFeatures([feature]);
        map.addLayer(layer);
        map.setCenter(new OpenLayers.LonLat(0, 0));


        // If a feature is to be modified, control.selectFeature gets called.
        // We want this test to fail if selectFeature gets called.
        var modified = false;

        var control = new OpenLayers.Control.ModifyFeature(layer);
        map.addControl(control);
        control.activate();

        // register a listener that will stop feature modification
        layer.events.on({"beforefeaturemodified": function() {return false}});

        // we can initiate feature modification by programmatically selecting
        // a feature
        control.selectFeature(feature);
        
        if(modified) {
            t.fail("selectFeature called, prepping feature for modification");
        } else {
            t.ok(true, "the beforefeaturemodified listener stopped feature modification");
        }
    }

    function test_selectFeature(t) {
        t.plan(12);
        var map = new OpenLayers.Map('map');
        var layer = new OpenLayers.Layer.Vector("Vectors!", {isBaseLayer: true});
        map.addLayer(layer);
        map.setCenter(new OpenLayers.LonLat(0, 0));
        var control = new OpenLayers.Control.ModifyFeature(layer);
        control.vertices = [];
        control.virtualVertices = [];
        var callback = function(obj) {
            t.ok(obj.feature == fakeFeature, "beforefeaturemodified triggered");
        };
        layer.events.on({"beforefeaturemodified": callback});
        control.onModificationStart = function(feature)  { t.eq(feature.id, fakeFeature.id, "On Modification Start called with correct feature."); } 
        
        // Start of testing
        
        control.collectVertices = function() { t.fail("Collect vertices called when geom is a point"); }
        var fakeFeature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 0));
        
        // Points don't call collectVertices
        control.selectFeature(fakeFeature);
        control.unselectFeature(fakeFeature);
        
        control.collectVertices = function() { 
          t.ok(true, "collectVertices called"); 
          this.vertices = 'a';
          this.virtualVertices = 'd';
          layer.addFeatures(this.vertices);
          layer.addFeatures(this.virtualVertices);
        }
        
        layer.addFeatures = function(features) { 
            t.ok(features == 'a' || features == 'd', "features passed correctly"); 
        }
        layer.destroyFeatures = function() {};

        fakeFeature.geometry = new OpenLayers.Geometry.Polygon([
            new OpenLayers.Geometry.LinearRing([
                new OpenLayers.Geometry.Point(0, 0),
                new OpenLayers.Geometry.Point(1, 1)
            ])
        ]);
        
        // OnSelect calls collectVertices and passes features to layer 
        control.selectFeature(fakeFeature);        
        control.unselectFeature(fakeFeature);
        layer.destroyFeatures = OpenLayers.Layer.Vector.prototype.destroyFeatures;
        
        control.vertices = ['a'];
        control.virtualVertices = [{destroy: function() {}}];
        
        layer.addFeatures = function(features) {} 
        
        layer.removeFeatures = function(features) { 
            t.eq(features.length, 1, "Correct feature length passed in");
        }    

        // Features are removed whenever they exist
        control.selectFeature(fakeFeature);
        
        control.destroy();

        // layer.destroy() will call removeFeatures with an empty array, make
        // removeFeatures reference an empty function to prevent the above
        // test to fail
        layer.removeFeatures = function(features) {};
        layer.destroy();
    }  

    function test_resetVertices(t) {
        t.plan(20);
        var layer = new OpenLayers.Layer.Vector();
        var control = new OpenLayers.Control.ModifyFeature(layer);
        var point = new OpenLayers.Geometry.Point(5,6);
        var point2 = new OpenLayers.Geometry.Point(7,8);
        var point3 = new OpenLayers.Geometry.Point(9,10);
        
        control.feature = new OpenLayers.Feature.Vector(point);
        control.resetVertices();
        t.eq(control.vertices.length, 0, "Correct vertices length");
        t.eq(control.virtualVertices.length, 0, "Correct virtual vertices length.");

        var multiPoint = new OpenLayers.Geometry.MultiPoint([point, point2]);
        control.feature = new OpenLayers.Feature.Vector(multiPoint);
        control.resetVertices();
        t.eq(control.vertices.length, 2, "Correct vertices length with multipoint");
        t.eq(control.virtualVertices.length, 0, "Correct virtual vertices length (multipoint).");

        var line = new OpenLayers.Geometry.LineString([point, point2]);
        control.feature = new OpenLayers.Feature.Vector(line);
        control.resetVertices();
        t.eq(control.vertices.length, 2, "Correct vertices length with line");
        t.eq(control.virtualVertices.length, 1, "Correct virtual vertices length (linestring).");
        
        var polygon = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([point, point2, point3])]);
        control.feature = new OpenLayers.Feature.Vector(polygon);
        control.resetVertices();
        t.eq(control.vertices.length, 3, "Correct vertices length with polygon");
        t.eq(control.virtualVertices.length, 3, "Correct virtual vertices length (polygon).");

        control.mode = OpenLayers.Control.ModifyFeature.DRAG;
        control.resetVertices();
        t.ok(control.dragHandle != null, "Drag handle is set");
        t.eq(control.vertices.length, 0, "Correct vertices length with polygon (DRAG)");

        control.mode = OpenLayers.Control.ModifyFeature.ROTATE;
        control.resetVertices();
        t.ok(control.radiusHandle != null, "Radius handle is set");
        t.eq(control.vertices.length, 0, "Correct vertices length with polygon (ROTATE)");

        control.mode = OpenLayers.Control.ModifyFeature.RESIZE;
        control.resetVertices();
        t.ok(control.radiusHandle != null, "Radius handle is set");
        t.eq(control.vertices.length, 0, "Correct vertices length with polygon (RESIZE)");

        control.mode = OpenLayers.Control.ModifyFeature.RESHAPE;
        control.resetVertices();
        t.ok(control.radiusHandle == null, "Radius handle is not set (RESHAPE)");
        t.eq(control.vertices.length, 3, "Correct vertices length with polygon (RESHAPE)");
        t.eq(control.virtualVertices.length, 3, "Correct virtual vertices length (RESHAPE)");
        
        control.mode = OpenLayers.Control.ModifyFeature.RESIZE | OpenLayers.Control.ModifyFeature.RESHAPE;
        control.resetVertices();
        t.ok(control.radiusHandle != null, "Radius handle is set (RESIZE|RESHAPE)");
        t.eq(control.vertices.length, 0, "No vertices when both resizing and reshaping (RESIZE|RESHAPE)");
        t.eq(control.virtualVertices.length, 0, "No virtual vertices when both resizing and reshaping (RESIZE|RESHAPE)");
        
        control.destroy();
        layer.destroy();
    }
    
    function test_dragVertex(t) {
        t.plan(8);
        var map = new OpenLayers.Map("map", {
            resolutions: [1]
        });
        var layer = new OpenLayers.Layer.Vector("foo", {
            maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
            isBaseLayer: true
        });
        map.addLayer(layer);
        
        var control = new OpenLayers.Control.ModifyFeature(layer);
        map.addControl(control);
        control.activate();
        
        map.zoomToMaxExtent();
        
        var log = {};
        layer.events.on({
            "vertexmodified": function(event) {
                log.event = event;
            }
        });
        
        // pretend to drag a point
        var feature = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(0, 0)
        );
        control.feature = feature;
        var pixel = new OpenLayers.Pixel(-100, 100);
        control.dragVertex(feature, pixel);
        t.eq(log.event.type, "vertexmodified", "[drag point] vertexmodified triggered");
        t.geom_eq(log.event.vertex, feature.geometry, "[drag point] listeners receive correct vertex");
        t.eq(log.event.feature.id, feature.id, "[drag point] listeners receive correct feature");
        t.ok(log.event.pixel === pixel, "[drag point] listeners receive correct pixel");
        
        // pretend to drag vertex of a linestring
        var vert = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(0, 0)
        );
        var feature = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.LineString([
                vert.geometry, new OpenLayers.Geometry.Point(10, 0)
            ])
        );
        control.feature = feature;
        var pixel = new OpenLayers.Pixel(-100, 100);
        control.dragVertex(vert, pixel);
        t.eq(log.event.type, "vertexmodified", "[drag vertex] vertexmodified triggered");
        t.geom_eq(log.event.vertex, vert.geometry, "[drag vertex] listeners receive correct vertex");
        t.eq(log.event.feature.id, feature.id, "[drag vertex] listeners receive correct feature");
        t.ok(log.event.pixel === pixel, "[drag vertex] listeners receive correct pixel");


        map.destroy();
    }
    function test_collectDragHandle(t) {
        t.plan(4);
        var map = new OpenLayers.Map("map", {
            resolutions: [1]
        });
        var layer = new OpenLayers.Layer.Vector("foo", {
            maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
            isBaseLayer: true
        });
        map.addLayer(layer);
        var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,1));
        layer.addFeatures([feature]);
        var control = new OpenLayers.Control.ModifyFeature(layer);
        map.addControl(control);
        control.activate();
        control.feature = feature;
        control.collectDragHandle();
        t.ok(control.dragHandle != null, "Drag handle created");
        t.ok(control.dragHandle._sketch == true, "Handle has _sketch true");
        t.ok(control.dragHandle.renderIntent == control.vertexRenderIntent,"Render intent for handle set");
        t.ok(control.layer.getFeatureById(control.dragHandle.id) != null, "Drag handle added to layer");
    }
    function test_collectRadiusHandle(t) {
        t.plan(4);
        var map = new OpenLayers.Map("map", {
            resolutions: [1]
        });
        var layer = new OpenLayers.Layer.Vector("foo", {
            maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
            isBaseLayer: true
        });
        map.addLayer(layer);
        var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,1));
        layer.addFeatures([feature]);
        var control = new OpenLayers.Control.ModifyFeature(layer);
        map.addControl(control);
        control.activate();
        control.feature = feature;
        control.collectRadiusHandle();
        t.ok(control.radiusHandle != null, "Radius handle created");
        t.ok(control.radiusHandle._sketch == true, "Radius has _sketch true");
        t.ok(control.radiusHandle.renderIntent == control.vertexRenderIntent,"Render intent for handle set");
        t.ok(control.layer.getFeatureById(control.radiusHandle.id) != null, "Drag radius added to layer");
    }
    function test_onDrag(t) {
        t.plan(1);
        t.ok(true, "onDrag not tested yet.");
    }
    
    function test_dragComplete(t) {
        t.plan(8);
        var layer = new OpenLayers.Layer.Vector();
        var control = new OpenLayers.Control.ModifyFeature(layer);
       
        var fakeFeature = {
         'geometry': { 'id':'myGeom'},
         'id': 'fakeFeature'
        };
        layer.addFeatures = function (verts) {
            t.ok(verts == 'virtual' || verts == 'normal', verts + " verts correct");
        }
        layer.removeFeatures = function (verts) {
            t.ok(verts == 'previous virtual' || verts == 'previous normal', verts + " verts correct");
        }
        layer.events.on({"featuremodified": function(event) {
            t.eq(event.feature, fakeFeature, "featuremodified triggered");
        }});
        control.onModification = function(feat) {
            t.eq(feat.id, fakeFeature.id, "onModification gets correct feat");
        }
        control.collectVertices = function() {
            t.ok(true, "collectVertices called");
            this.vertices = 'normal';
            this.virtualVertices = 'virtual';
            layer.addFeatures(this.vertices);
            layer.addFeatures(this.virtualVertices);
        }
        control.feature = fakeFeature;
        control.vertices = 'previous normal';
        control.virtualVertices = 'previous virtual';
        control.dragComplete();
        t.eq(fakeFeature.state, OpenLayers.State.UPDATE, "feature state set to UPDATE");
        
        control.destroy();

        // layer.destroy() will call removeFeatures with an empty array, make
        // removeFeatures reference an empty function to prevent the above
        // test to fail
        layer.removeFeatures = function(verts) {};
        layer.destroy();
    }
    
    function test_deactivate(t) {
        t.plan(2);
        var map = new OpenLayers.Map("map");
        var layer = new OpenLayers.Layer.Vector();
        map.addLayer(layer);
        var control = new OpenLayers.Control.ModifyFeature(layer);
        map.addControl(control);
        
        control.handlers.keyboard.deactivate = function() {
            t.ok(true,
                 "control.deactivate calls deactivate on keyboard handler");
        }
        control.handlers.drag.deactivate = function() {
            t.ok(true,
                 "control.deactivate calls deactivate on drag handler");
        }
        control.active = true;
        control.deactivate();
        
        control.handlers.keyboard.deactivate = OpenLayers.Handler.Keyboard.prototype.deactivate;
        control.handlers.drag.deactivate = OpenLayers.Handler.Drag.prototype.deactivate;
        map.destroy();
    }

    function test_onModificationStart(t) {
        t.plan(5);
        var map = new OpenLayers.Map("map");
        var layer = new OpenLayers.Layer.Vector(null, {
            styleMap: new OpenLayers.StyleMap({
                "vertex": new OpenLayers.Style({foo: "bar"})
            }, {extendDefault: false})
        });
        map.addLayer(layer);
        var control = new OpenLayers.Control.ModifyFeature(layer);
        map.addControl(control);
        control.activate();
        
        // make sure onModificationStart is called on feature selection
        var testFeature = new OpenLayers.Feature.Vector(
            OpenLayers.Geometry.fromWKT("LINESTRING(3 4,10 50,20 25)")
        );
        layer.addFeatures([testFeature]);
        control.onModificationStart = function(feature) {
            t.eq(feature.id, testFeature.id,
                 "onModificationStart called with the right feature");
        };
        control.selectFeature(testFeature);
        
        // make sure styles are set correctly from default style
        t.eq(control.virtualStyle, OpenLayers.Util.applyDefaults({
            strokeOpacity: 0.3,
            fillOpacity: 0.3
        }, OpenLayers.Feature.Vector.style["default"]), "virtual style set correctly");
        var vertex = layer.features[layer.features.length-1];
        t.eq(vertex.renderIntent, null, "vertex style set correctly - uses default style");
        control.unselectFeature(testFeature);
        
        // make sure styles are set correctly with vertexRenderIntent
        control = new OpenLayers.Control.ModifyFeature(layer, {vertexRenderIntent: "vertex"});
        map.addControl(control);
        control.activate();
        control.selectFeature(testFeature);
        t.eq(control.virtualStyle, {
            strokeOpacity: 0.3,
            fillOpacity: 0.3,
            foo: "bar"
        }, "virtual style set correctly");
        var vertex = layer.features[layer.features.length-1];
        t.eq(vertex.renderIntent, "vertex", "vertex style set correctly - uses 'vertex' renderIntent");
        control.unselectFeature(testFeature);
        
        map.destroy();
    }
    
    function test_onModification(t) {
        t.plan(3);
        var map = new OpenLayers.Map("map");
        var layer = new OpenLayers.Layer.Vector();
        map.addLayer(layer);
        var control = new OpenLayers.Control.ModifyFeature(layer);
        map.addControl(control);
        control.activate();
        
        // make sure onModification is called on drag complete
        var point = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(Math.random(), Math.random())
        );
        control.feature = point;
        control.onModification = function(feature) {
            t.eq(feature.id, point.id,
                "onModification called with the right feature on drag complete");
        };
        control.dragComplete();
        
        // make sure onModification is called on vertex deletion
        var poly = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Polygon()
        );
        var oldDraw = layer.drawFeature;
        layer.drawFeature = function() {
            return;
        };
        control.feature = poly;
        control.vertices = [point];
        layer.events.on({"featuremodified": function(event) {
            t.eq(event.feature.id, poly.id, "featuremodified triggered");
        }});

        control.onModification = function(feature) {
            t.eq(feature.id, poly.id,
                "onModification called with the right feature on vertex delete");
        };
        point.geometry.parent = poly.geometry;
        origGetFeatureFromEvent = layer.getFeatureFromEvent;
        layer.getFeatureFromEvent = function() { return point; };
        control.handleKeypress({keyCode:46});
        layer.drawFeature = oldDraw;
        layer.getFeatureFromEvent = origGetFeatureFromEvent;
        
        map.destroy();
    }

    function test_onModificationEnd(t) {
        t.plan(3);
        var map = new OpenLayers.Map("map");
        var layer = new OpenLayers.Layer.Vector();
        map.addLayer(layer);
        var control = new OpenLayers.Control.ModifyFeature(layer);
        map.addControl(control);
        control.activate();
        
        // make sure onModificationEnd is called on unselect feature
        var testFeature = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(Math.random(), Math.random())
        );
        layer.events.on({"afterfeaturemodified": function(event) {
            t.eq(event.feature.id, testFeature.id, "afterfeaturemodified triggered");
            t.eq(event.modified, false, "afterfeaturemodified event given proper modified property (false - feature was not modified in this case)");
        }});
        control.onModificationEnd = function(feature) {
            t.eq(feature.id, testFeature.id,
                 "onModificationEnd called with the right feature");
        };
        control.unselectFeature(testFeature);
        
        map.destroy();
    }
    
    function test_events(t) {
        t.plan(2);
        var map = new OpenLayers.Map("map");
        var layer = new OpenLayers.Layer.Vector();
        map.addLayer(layer);
        var control = new OpenLayers.Control.ModifyFeature(layer);
        map.addControl(control);
        control.activate();
        
        // make sure onModificationStart is called on feature selection
        var testFeature = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(Math.random(), Math.random())
        );
        
        // test that beforefeatureselected is triggered
        function handle_beforefeatureselected(event) {
            t.ok(event.feature == testFeature, "beforefeatureselected called with the correct feature");
        }
        layer.events.on({
            "beforefeatureselected": handle_beforefeatureselected
        });
        layer.events.triggerEvent("beforefeatureselected", {
            feature: testFeature
        });
        layer.events.un({
            "beforefeatureselected": handle_beforefeatureselected
        });
        
        // test that beforefeatureselected is triggered
        function handle_featureselected(event) {
            t.ok(event.feature == testFeature, "featureselected called with the correct feature");
        }
        layer.events.on({
            "featureselected": handle_featureselected
        });
        layer.events.triggerEvent("featureselected", {
            feature: testFeature
        });
        layer.events.un({
            "featureselected": handle_featureselected
        });

        map.destroy();
    }
    
    function test_standalone(t) {
        
        t.plan(17);
        var map = new OpenLayers.Map("map");
        var layer = new OpenLayers.Layer.Vector();
        
        var f1 = new OpenLayers.Feature.Vector(
            OpenLayers.Geometry.fromWKT("LINESTRING(3 4,10 50,20 25)")
        );
        var f2 = new OpenLayers.Feature.Vector(
            OpenLayers.Geometry.fromWKT("POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2, 3 2, 3 3, 2 3,2 2))")
        );
        var f3 = new OpenLayers.Feature.Vector(
            OpenLayers.Geometry.fromWKT("POINT(10 15)")
        );
        var f4 = new OpenLayers.Feature.Vector(
            OpenLayers.Geometry.fromWKT("POINT(15 10)")
        );
        layer.addFeatures([f1, f2, f3, f4]);
        
        map.addLayer(layer);
        var control = new OpenLayers.Control.ModifyFeature(layer, {standalone: true});
        map.addControl(control);
        
        var log = [];
        layer.events.on({
            beforefeaturemodified: function(evt) {
                layer.events.unregister("beforefeaturemodified", this, arguments.callee);
                log.push(evt);
            },
            featuremodified: function(evt) {
                log.push(evt);
            },
            afterfeaturemodified: function(evt) {
                log.push(evt);
            }
        });
        
        // activate control
        control.activate();
        t.eq(control.active, true, "[activate] control activated");
        
        // manually select feature for editing
        control.selectFeature(f1);
        t.eq(log.length, 1, "[select f1] beforefeaturemodified triggered");
        t.ok(control.feature === f1, "[select f1] control.feature set to f1");
        log = []
        
        // manually unselect feature for editing
        control.unselectFeature(f1);
        t.eq(control.feature, null, "[unselect f1] control.feature set to null");
        t.eq(log.length, 1, "[unselect f1] event logged");
        t.eq(log[0].type, "afterfeaturemodified", "[unselect f1] afterfeaturemodified triggered");
        t.ok(log[0].feature === f1, "[unselect f1] correct feature");
        t.eq(log[0].modified, false, "[unselect f1] feature not actually modified");
        
        // clear log and select new feature for editing
        log = [];
        control.selectFeature(f2);
        t.ok(control.feature === f2, "[select f2] control.feature set to f2");
                
        // deactivate control and confirm feature is unselected
        control.deactivate();
        t.eq(log.length, 1, "[deactivate] event logged");
        t.eq(log[0].type, "afterfeaturemodified", "[deactivate] afterfeaturemodified triggered");
        t.ok(log[0].feature === f2, "[deactivate] correct feature");
        t.eq(log[0].modified, false, "[deactivate] feature not actually modified");
        
        // select the polygon feature to make sure that we can drag vertices and
        // virtual vertices
        control.selectFeature(f2);
        var origGetFeatureFromEvent = layer.getFeatureFromEvent;
        layer.getFeatureFromEvent = function() { return control.vertices[0]; };
        control.handlers.drag.callbacks.down.call(control, new OpenLayers.Pixel(0,0));
        t.ok(control.vertex === control.vertices[0], "can drag vertex of feature f2");
        t.ok(control.feature === f2, "dragging a vertex does not change the selected feature");
        layer.getFeatureFromEvent = function() { return control.virtualVertices[0]; };
        control.handlers.drag.callbacks.down.call(control, new OpenLayers.Pixel(0,0));
        t.ok(control.vertex === control.virtualVertices[0], "can drag virtual vertex of feature f2");
        t.ok(control.feature === f2, "dragging a vertex does not change the selected feature");
        layer.getFeatureFromEvent = origGetFeatureFromEvent;
        control.deactivate();
        
        map.destroy();
        
    }
    
    function test_setFeatureState(t) {
        t.plan(4);
        var map = new OpenLayers.Map("map");
        var layer = new OpenLayers.Layer.Vector("vector", {isBaseLayer: true});
        map.addLayer(layer);
        var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,2));
        layer.addFeatures([feature]);
        var control = new OpenLayers.Control.ModifyFeature(layer, {standalone: true});
        map.addControl(control);
        
        control.selectFeature(feature);
        var originalGeometry = feature.geometry;
        
        t.ok(control._originalGeometry, "original geometry stored for later use in setFeatureState");
        
        feature.geometry = new OpenLayers.Geometry.Point(2,3);
        control.modified = true;
        control.setFeatureState();
        
        t.eq(feature.state, OpenLayers.State.UPDATE, "feature state set to UPDATE");
        t.geom_eq(feature.modified.geometry, originalGeometry, "original geometry stored on the modified property");
        t.eq(control._originalGeometry, undefined, "original geometry deleted once it is set on the modified property");
    }

    function test_createVertices(t) {
        t.plan(2);
        var layer = new OpenLayers.Layer.Vector();
        var control = new OpenLayers.Control.ModifyFeature(layer, {
            createVertices: false
        });
        var line = new OpenLayers.Geometry.LineString([
            new OpenLayers.Geometry.Point(5, 6),
            new OpenLayers.Geometry.Point(7, 8),
            new OpenLayers.Geometry.Point(9, 10)
        ]);
        control.feature = new OpenLayers.Feature.Vector(line);
        control.resetVertices();

        t.eq(control.vertices.length, 3, "Correct vertices length with createVertices is false");
        t.eq(control.virtualVertices.length, 0, "Correct virtual vertices length with createVertices is false");
        control.destroy();
    }

    function test_moveLayerToTop_moveLayerBack(t) {
        t.plan(2);
        var map = new OpenLayers.Map("map");
        var layer1 = new OpenLayers.Layer.Vector();
        var layer2 = new OpenLayers.Layer.Vector();
        map.addLayers([layer1, layer2]);
        var control = new OpenLayers.Control.ModifyFeature(layer1);
        map.addControl(control);
        control.activate();
        t.ok(layer1.div.style.zIndex > layer2.div.style.zIndex, "layer raised so events don't get swallowed");
        control.deactivate();
        t.ok(layer1.div.style.zIndex < layer2.div.style.zIndex, 'layer order restored on deactivation');
    }

    </script>
</head>
<body>
    <div id="map" style="width: 400px; height: 250px;"/>
</body>
</html>
